if (!Array.prototype.remByVal) {
	Array.prototype.remByVal = function (val) {
		for (var i = 0; i < this.length; i++) {
			if (this[i] === val) {
				this.splice(i, 1);
				i--;
			}
		}
		return this;
	};
}

class nMuxerSegmentInfo {
	currentVideoLevel: nMuxerLevel;
	currentAudioLevel: nMuxerLevel;
}

class nMuxerLevel {
	url: string;
	frameRate: number;
	width: number;
	height: number;
	bitrate: number;
	codecMime: string;
}

class nMuxerInitInfo {
	videoElement: HTMLVideoElement;
	videoLevels: Array<nMuxerLevel>;
	audioLevels: Array<nMuxerLevel>;
}

function __is_valid_array<T>(arr: Array<T>) {
	return arr !== undefined && arr !== null && arr.length > 0;
}

function __has_mse_apis() {
	return !!window.MediaSource || !!window.ManagedMediaSource;
}

function makeMediaSource() {
	if (window.ManagedMediaSource) {
		return new ManagedMediaSource();
	} else if (window.MediaStream) {
		return new MediaSource();
	} else {
		throw new Error("E:MISSING_MSE_API");
	}
}

function __check_and_remove_unsupported_codecs(arr: Array<nMuxerLevel>) {
	for (const x of arr) {
		if (!MediaSource.isTypeSupported(x["codecMime"])) {
			arr.remByVal(x);
		}
	}
	if (arr.length === 0) throw new Error("E:NO_VALID_LEVELS_FOUND");
}

function __fetch_initial_chunk(level: nMuxerLevel) {}

function __guessInitialLevel(arr: Array<nMuxerLevel>, sbFor: string): nMuxerLevel {
	// TODO
	return new nMuxerLevel();
}

const NMUXER_VERSION = "2023-06-09";
const MMUXER_HAS_ASYNC_APPEND = window.SourceBuffer && !!SourceBuffer.prototype.appendBufferAsync;
function nMuxer(initInfo: nMuxerInitInfo) {
	if (!initInfo["videoElement"] || !__is_valid_array(initInfo["audioLevels"]) || !__is_valid_array(initInfo["videoLevels"])) {
		throw new Error("E:INVALID_INIT_INFO");
	}
	if (!__has_mse_apis()) {
		throw new Error("E:MISSING_MSE_API");
	}
	__check_and_remove_unsupported_codecs(initInfo["audioLevels"]);
	__check_and_remove_unsupported_codecs(initInfo["videoLevels"]);
	const mediaSource = makeMediaSource();
	initInfo["videoElement"].src = URL.createObjectURL(mediaSource);
	const sourceBuffers = { audio: null, video: null };
	function __nMuxer_source_buffer(sbFor: string) {
		var currentLevel = __guessInitialLevel(sbFor === "video" ? initInfo["videoLevels"] : initInfo["audioLevels"], sbFor);
		const sourceBuffer = mediaSource.addSourceBuffer(currentLevel["codecMime"]);
		sourceBuffer[sbFor] = sourceBuffer;
		var initChunk = __fetch_initial_chunk(currentLevel);
		// submitChunk()
		// signalVideoPlayback({on: sourceBuffers, type: "ready"})
		// enterManagedBuffersMode()
	}
	function __nMuxer_sourceOpen() {
		/* TODO: Use libWeb.js */
		new Promise(() => {
			__nMuxer_source_buffer("video");
		});
		new Promise(() => {
			__nMuxer_source_buffer("audio");
		});
	}
	mediaSource.addEventListener("sourceopen", __nMuxer_sourceOpen);
	return {
		version: NMUXER_VERSION,
		mediaSource: mediaSource,
		sourceBuffers: sourceBuffers,
	};
}
